package com.livestocksolutions;

import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;

import com.livestocksolutions.util.UpdateHistory;

/* -- Version History --
 *  1.0 : 12 July 2012 - Initial version.
 *  
 *  1.1 : 12 July 2012 - Changed Observation[] to ArrayList<Observation>
 *						 Changed getUnit() to getUnitString(int) to return the String version
 *                        of the Area Units, getUnit() now returns the Area as is, in integer
 *                        format.
 *  
 *  1.2 : 16 July 2012 - Class now implements Serializable
 */

/**
 * This class specifically relates to the storage of a Paddock details.
 * @author Adam Parr
 * @version 1.2
 * 
 * @.modified 24 September 2012
 */
public class Paddock extends Location {
	/**
	 * Hectare 
	 */
	public static final int HA = 0;
	/**
	 * Acre 
	 */
	public static final int ACRE = 1;
	/**
	 * Kilometre
	 */
	public static final int KM = 2;
	/**
	 * Metre
	 */
	public static final int METRE = 3;
	
	/**
	 * The area of the paddock
	 */
	private double area;
	
	/**
	 * An Integer representation of the units of the area.<br>
	 * A value of either {@link #HA}, {@link #ACRE}, {@link #KM}
	 * or {@link #METRE}
	 */
	private int units;
	
	/**
	 * History of any chemicals used on this paddock
	 */
	protected HashMap<Long,ChemicalHistory> chemicalsUsed = new HashMap<Long,ChemicalHistory>();

	/**
	 * The safe grazing date of this paddock based on the latest
	 * date of the chemicals used on this paddock
	 */
	private long safeGrazingDate;
	
	
	/**
	 * Checks to see if the safe grazing date is a date in the past
	 * @return <code>true</code> if no chemical has been used on this
	 * paddock or the safe grazing date of any and all of the chemicals
	 * used on this paddock is a date in the past, <code>false</code>
	 * would otherwise be returned if a date is still yet to come.
	 */
	public boolean isSafeToUse(){
		if(safeGrazingDate!=0 && new Date(safeGrazingDate).after(new Date())){
			return false;
		}
		return true;
	}
	
	/**
	 * Get the area of this paddock
	 * @return The area of this paddock
	 */
	public double getArea(){
		return area;
	}
	
	/**
	 * Returns the date this paddock is supposed to be considered safe for grazing
	 * @return A date when this paddock is considered to be safe for grazing
	 */
	public Date getSafeGrazingDate(){
		return safeGrazingDate==0?null:new Date(safeGrazingDate);
	}
	
	/**
	 * Get an array of all chemicals used on this paddock
	 * @return an array of chemicals used on this paddock
	 */
	public ChemicalHistory[] getChemicalsUsed(){
		return chemicalsUsed.values().toArray(new ChemicalHistory[]{});
	}
	
	/**
	 * Get an integer value that represents the unit the area of this paddock is measured in
	 * @return An integer that represents the unit the area of this paddock is measured in
	 */
	public int getUnits(){
		return units;
	}
	
	/**
	 * Get a String representation of the Units of this paddocks area
	 * @param unit an integer of the unit to return the name for
	 * @return A String of the Area Units name
	 */
	public static String getUnitString(int unit){
		switch(unit){
			case HA:
				return "Hectare";
			case ACRE:
				return "Acre";
			case KM:
				return "Kilometre";
			case METRE:
				return "Metre";
			default:
				return "Unknown";
		}
	}
	
	/**
	 * Sets the area of this paddock to the double provided
	 * @param area the size of this paddock
	 * @see #setUnits(int) to change the units of the area value.
	 */
	public void setArea(double area){
		if(this.area!=area){
			this.area=area;
			update.put("AREA", new UpdateHistory(String.valueOf(area)));
		}
	}
	
	/**
	 * Set the units of the area to one of the available choices from 
	 * either {@link #HA}, {@link #ACRE}, {@link #KM} or {@link #METRE}
	 * @param unit integer representation of this paddocks area unit
	 * @return <code>true</code> if unit was a valid integer, <code>false</code> otherwise
	 */
	public boolean setUnits(int unit){
		switch(unit){
			case HA:
			case ACRE:
			case KM:
			case METRE:
				units=unit;
				update.put("UNITS", new UpdateHistory(String.valueOf(unit)));
				return true;
			default:
				return false;
		}
	}
	
	/**
	 * Add or updates a new chemical that has been used on this paddock.
	 * @param chemicalUsed the chemical that was used.
	 */
	public void addChemical(ChemicalHistory chemicalUsed){
		if(chemicalUsed.getID()==0){
			// Adding a new Contact
			long newID = new Date().getTime();
			while(chemicalsUsed.containsKey(newID)){
				newID++;
			}
			chemicalUsed.setID(newID);
		}
		if(!chemicalsUsed.containsKey(chemicalUsed.getID())){
			chemicalsUsed.put(chemicalUsed.getID(),chemicalUsed);
			
			UpdateHistory chemicalList = update.get("CHEMICAL")!=null? update.get("CHEMICAL"):new UpdateHistory();
			chemicalList.setValues(String.valueOf(chemicalUsed.getID()),new UpdateHistory("true"));
			update.put("CHEMICAL", chemicalList);
		}

		Calendar c = Calendar.getInstance();
		c.add(Calendar.MINUTE, (int)(((Chemical)chemicalUsed.getItem()).getWithholdPeriod()/1000)/60);
		
		if(safeGrazingDate==0 || new Date(safeGrazingDate).before(c.getTime())){
			safeGrazingDate = c.getTime().getTime();
			update.put("SAFEGRAZINGDATE", new UpdateHistory(String.valueOf(safeGrazingDate)));
		}
	}
	
	/**
	 * Adds a new chemical that has been used on this paddock
	 * @param chemical the chemical that was used
	 * @param quantity the amount that was used
	 * @param measurement the units that quantity was measured in
	 * @deprecated Please use {@link #addChemical(ChemicalHistory)} instead.
	 */
	@Deprecated
	public void setUsedChemical(Chemical chemical, double quantity, String measurement){
		ChemicalHistory ch = new ChemicalHistory(chemical,quantity,measurement);
		ch.setDate(new Date());
		
		addChemical(ch);
	}
	
	/**
	 * Updates this Object with the updated values from the different version.
	 * @param changes Paddock that represents this Object in a different version.
	 * @return <tt>true</tt>.
	 */
	public boolean update(Paddock changes){
		if(changes!=null && this!=changes){
			super.update(changes);
			if(updateBefore("SAFEGRAZINGDATE",changes)){
				safeGrazingDate=Long.parseLong(changes.update.get("SAFEGRAZINGDATE").getValue());
				update.put("SAFEGRAZINGDATE", new UpdateHistory(String.valueOf(safeGrazingDate)));
			}
			
			if(updateBefore("AREA",changes)){
				area=Double.parseDouble(changes.update.get("AREA").getValue());
				update.put("AREA", new UpdateHistory(String.valueOf(units)));
			}
			
			if(updateBefore("UNITS",changes)){
				units=Integer.parseInt(changes.update.get("UNITS").getValue());
				update.put("UNITS", new UpdateHistory(String.valueOf(units)));
			}
			
			if(changes.update.get("CHEMICAL")!=null){
				for(String key : changes.update.get("CHEMICAL").toKeyArray()){
					if(update.get("CHEMICAL")==null || update.get("CHEMICAL").getValue(key)==null || !update.get("CHEMICAL").getValue(key).getDate().after(changes.update.get("CHEMICAL").getValue(key).getDate())){
						UpdateHistory medicineList = update.get("CHEMICAL")!=null?update.get("CHEMICAL"):new UpdateHistory();
						ChemicalHistory m=null;
						if((m =chemicalsUsed.get(Long.parseLong(key)))!=null){
							if(changes.update.get("CHEMICAL").getValue(key).equals("false")){
								// Remove
								chemicalsUsed.remove(m);
								medicineList.setValues(key,new UpdateHistory("false"));
							}else{
								// Add
								m.update(changes.chemicalsUsed.get(key));
								chemicalsUsed.put(m.getID(),m);
								medicineList.setValues(key,new UpdateHistory("true"));
							}
						}else if(changes.update.get("CHEMICAL").getValue(key).equals("true")){
							m=changes.chemicalsUsed.get(key);
							chemicalsUsed.put(m.getID(),m);
						}
						update.put("CHEMICAL", medicineList);
					}
				}
			}
			return true;
		}
		return false;
	}
}
